/******************************************************************************
 * \brief	队列（FIFO、Queue、缓存）的实现
 * \details	不能单独使用，不包含实际的缓存数据，需要和上层结构体结合在一起使用；
 *			既适用于操作系统中，也适用于嵌入式裸机中
 * \remarks Linux原生的队列kfifo一次只能操作一个队列，操作变长元素时，判断队列里
 *			元素个数不容易，在复杂场景下使用不方便；消息队列msgbuf的实现又太复杂，
 *			所以在这里自行实现FIFO队列（queue）模块
 * \note	File format: UTF-8，中文编码：UTF-8
 * \author	将狼才鲸
 * \date	2023-03-08
 ******************************************************************************/

/*********************************** 头文件 ***********************************/
#include "queue.h"

/********************************** 接口函数 **********************************/
/**
 * \brief	初始化队列入口，这时候整个队列是空的
 */
void queue_entry_init(queue_entry_t *pqe)
{
	if (!pqe)
		return;

	/* C语言优先级顺序：结合类 > 单目运算 > 算数运算 > 移位运算 > 判断
					  位逻辑运算 > 逻辑运算 > 赋值 > 逗号运算
	   [] () . -> 高于 -负号 (类型转换) ++前置后置 --前置后置
	   * & ！ ~ sizeof 高于 算数运算 */
	queue_lock_init(&pqe->lock);

	queue_lock(&pqe->lock);
	/* 初始化队列中的已释放链表和已使用链表 */
	for (int i = 0; i < QUEUE_MEMBER_MAX; i++) {
		init_list_head(&pqe->lists[i]);
	}
	queue_unlock(&pqe->lock);
}

/**
 * \brief	初始化一个队列中的元素
 * \details	该函数较少被使用
 */
void queue_init(queue_t *pq)
{
	if (!pq)
		return;

	init_list_head(&pq->node);
	pq->queue_entry = NULL;
}

/**
 * \brief	将队列元素推入到其它队列，或推入到自己队列的已使用链表中或已释放链表中
 * \param	pqe:	为空时表示推入到自己元素所在的队列，非空代表要推入的队列（跨队列操作）
 * \param	pq:		要推入的队列元素
 * \param	line:	推入到哪个链表：已使用链表或已释放链表，QUEUE_USED or QUEUE_FREE
 */
int queue_put(queue_entry_t *pqe, queue_t *pq, int line)
{
	list_head_t *node, *head;

	if (!pq || line >= QUEUE_MEMBER_MAX)
		return -EPERM;

	if (!pqe)
		pqe = pq->queue_entry;
	if (!pqe)
		return -EPERM;

	queue_lock(&pqe->lock);

	node = &pq->node;			/* 当前队列节点 */
	head = &pqe->lists[line];	/* 队列中要要获取或者释放的入口 */

	list_del_init(node);		/* 删除node原来的链表，并重新初始化node这个链表 */
	list_add_tail(node, head);	/* 加到链表尾，先入先出 */

	queue_unlock(&pqe->lock);

	return 0;
}

/**
 * \brief	从队列入口的已使用链表或已释放链表中获取一个队列元素
 * \param	line:	哪个链表：已使用链表或已释放链表，QUEUE_USED or QUEUE_FREE
 * \return	获取到的队列节点；获取失败则返回NULL
 */
queue_t *queue_get(queue_entry_t *pqe, int line)
{
	queue_t *pq;
	list_head_t *node, *head;

	if (!pqe || line >= QUEUE_MEMBER_MAX)
		return NULL;

	queue_lock(&pqe->lock);

	head = &pqe->lists[line];	/* 队列中要要获取或者释放的入口 */

	pq = NULL;
	if (!list_empty_careful(head)) {
		pq = list_first_entry(head, queue_t, node);	/* 获取队列的第一个元素 */
		node = &pq->node;
		list_del_init(node);	/* 销毁该元素 */
	}

	queue_unlock(&pqe->lock);

	return pq;
}

/**
 * \brief	预览已使用或者已释放链表中的第一个元素
 * \details	该元素预览后不会被弹出
 * \param	line:	QUEUE_USED or QUEUE_FREE
 */
queue_t *queue_peek(queue_entry_t *pqe, int line)
{
	queue_t *pq;
	list_head_t *head;

	if (!pqe || line >= QUEUE_MEMBER_MAX)
		return NULL;

	queue_lock(&pqe->lock);

	head = &pqe->lists[line];	/* 队列中要要获取或者释放的入口 */

	pq = NULL;
	if (!list_empty_careful(head)) {
		pq = list_first_entry(head, queue_t, node);	/* 获取队列的第一个元素 */
	}

	queue_unlock(&(pqe->lock));

	return pq;
}

/**
 * \brief	获取已使用或者已释放链表中剩余的元素个数
 * \param	line:	QUEUE_USED or QUEUE_FREE
 */
int queue_count(queue_entry_t *pqe, int line)
{
	int count = 0;
	list_head_t *node, *n, *head;

	if (!pqe || line >= QUEUE_MEMBER_MAX)
		return -EPERM;

	queue_lock(&pqe->lock);

	head = &pqe->lists[line];	/* 队列中要要获取或者释放的入口 */

	if (!list_empty_careful(head)) {
		list_for_each_safe(node, n, head) {
			count++;
		}
	}

	queue_unlock(&pqe->lock);

	return count;
}

/*********************************** 文件尾 ***********************************/
